#!/usr/bin/env python2

# Copyright (c) 2010 Anil Kumar
# All rights reserved.
#
# License: BSD 

import os
import re
import array

from PyQt4 import QtGui
from PyQt4.QtGui import *
from PyQt4.QtCore import *

import DialogManager

from EdPage import EditorPage

class EditorBook(QTabWidget):
	sig_file_closed = pyqtSignal(str)
	sig_history_update = pyqtSignal(str, int)
	sig_tab_changed = pyqtSignal(str)
	sig_open_dir_view = pyqtSignal(str)
	sig_editor_text_selected = pyqtSignal(str)

	def __init__(self, *args):
		apply(QTabWidget.__init__,(self, ) + args)

		self.setMovable(True)
		self.setTabsClosable(True)
		self.tabCloseRequested.connect(self.removeTab)
		self.currentChanged.connect(self.tab_change_cb)
		
		self.is_show_line = False
		self.is_show_folds = False
		self.f_text = None
		self.ev_font = "Monospace,10,-1,5,50,0,0,0,0,0"

	def new_editor_page(self):
		return EditorPage()

	def addFile(self, fileName):
		ed = self.new_editor_page()
		ed.open_file(fileName)
		print  'NOTE: must apply font linenumber or other settings when open a new editor page.'
		#ed.ev.set_font(self.ev_font)
		#ed.ev.show_line_number_cb(self.is_show_line)
		#ed.ev.show_folds_cb(self.is_show_folds)
		self.addTab(ed, os.path.basename(fileName))
		return ed

	def search_already_opened_files(self, filename):
		for i in range(self.count()):
			page = self.widget(i)
			if (page.get_filename() == filename):
				return page
		return None

	def removeTab(self, inx):
		ed = self.widget(inx)
		f = ed.ev.get_filename()
		QTabWidget.removeTab(self, inx)
		self.sig_file_closed.emit(f)

	def clear(self):
		while self.count() != 0:
			self.removeTab(0)

	def remove_tab_list(self, inx_list):
		for inx in sorted(inx_list, reverse=True):
			self.removeTab(inx)

	def tab_list(self, inx, type):
		inx_list = []
		if type == 'all' or type == 'files':
			return range(self.count())
		if type == 'left':
			return range(inx)
		if type == 'right':
			return range(inx + 1, self.count())
		if type == 'other':
			return self.tab_list(inx, 'left') + self.tab_list(inx, 'right')
		assert 0

	def close_list_common(self, type):
		inx_list = self.tab_list(self.currentIndex(), type)
		if len(inx_list) == 0:
			return
		if not DialogManager.show_yes_no('Close all %s ?' % type):
			return
		self.remove_tab_list(inx_list)

	def close_all_left_cb(self):
		self.close_list_common('left')
	def close_all_right_cb(self):
		self.close_list_common('right')
	def close_all_other_cb(self):
		self.close_list_common('other')
	def close_all_cb(self):
		self.close_list_common('files')

	def get_current_word(self):
		ed = self.currentWidget()
		if not ed:
			return
		if ed.ev.hasSelectedText():
			return ed.ev.selectedText()
		(line, index) = ed.ev.getCursorPosition()
		text = ed.ev.text(line)
		# Go left
		linx = index
		while linx > 0 and ed.ev.isWordCharacter(text[linx - 1]):
			linx = linx - 1
		# Go right
		rinx = index
		while rinx < len(text) and ed.ev.isWordCharacter(text[rinx]):
			rinx = rinx + 1
		
		text = text[linx:rinx]
		if text == '':
			return None
		return text
	def get_current_file_line(self):
		ed = self.currentWidget()
		if not ed:
			return (None, None)
		(line, inx) = ed.ev.getCursorPosition()
		return (ed.ev.filename, line + 1)
	def get_file_line_list(self):
		fl_list = []
		tlist = range(self.count())
		inx = self.currentIndex()
		if inx >= 0:
			tlist.append(inx)
		for inx in tlist:
			ed = self.widget(inx)
			(line, inx) = ed.ev.getCursorPosition()
			fl_list.append('%s:%d' % (ed.ev.filename, line + 1))
		return fl_list

	def matching_brace_cb(self):
		ed = self.currentWidget()
		if ed:
			ed.ev.moveToMatchingBrace()
			#ed.ev.setFocus()
	def goto_line_cb(self):
		ed = self.currentWidget()
		if not ed:
			return (None, None)
		(line, inx) = ed.ev.getCursorPosition()
		#return (line + 1, ed.ev.lines())
		line = DialogManager.show_goto_line_dialog(line + 1, ed.ev.lines())
		if (line == None):
			return
		ed.ev.goto_line(line)	

	def focus_editor(self):
		page = self.currentWidget()
		if page:
			page.ev.setFocus()

	def close_current_page(self):
		self.removeTab(self.currentIndex())
		self.focus_editor()

	def focus_search_ctags(self):
		ed = self.currentWidget()
		if ed:
			ed.fcv.focus_search_ctags()
		
	def copy_edit_cb(self):
		ed = self.currentWidget()
		if ed:
			ed.ev.copy()

	def tab_change_cb(self, inx):
		if (inx == -1):
			fname = ''
		else:
			page = self.currentWidget()
			page.ev.setFocus()
			fname = page.get_filename()
		self.sig_tab_changed.emit(fname)
			
	def open_dir_cb(self):
		page = self.currentWidget()
		if page:
			fname = page.get_filename()
			self.sig_open_dir_view.emit(fname)
			

	def mousePressEvent(self, m_ev):
		QTabWidget.mousePressEvent(self, m_ev)
		if (m_ev.button() == Qt.RightButton):
			# setup popup menu
			self.pmenu = QMenu()
			self.pmenu.addAction("Open dir", self.open_dir_cb)
			self.pmenu.addSeparator()
			self.pmenu.addAction("Close All &Left", self.close_all_left_cb)
			self.pmenu.addAction("Close All &Right", self.close_all_right_cb)
			self.pmenu.addAction("Close &Others", self.close_all_other_cb)
			self.pmenu.addSeparator()
			self.pmenu.addAction("Close &All", self.close_all_cb)
			self.pmenu.exec_(QCursor.pos())

	def show_file_line(self, filename, line, hist=True):
		if line:
			(f, l) = self.get_current_file_line()
			if (f):
				if hist:
					self.sig_history_update.emit(f, l)
		filename = str(filename)
		if (not os.path.exists(filename)):
			return
		page = self.search_already_opened_files(filename)
		if page == None:
			page = self.addFile(filename)
		self.setCurrentWidget(page)
		if line:
			page.ev.goto_line(line)
			if hist:
				self.sig_history_update.emit(filename, line)
		page.ev.setFocus()
		# text selected callback: need to send out again.
		page.ev.sig_text_selected.connect(self.editor_text_selected)

	def editor_text_selected(self, text):
		self.sig_editor_text_selected.emit(text)

	def show_file(self, filename):
		self.show_file_line(filename, 0)

	def show_line(self, line):
		ed = self.currentWidget()
		if not ed:
			return
		ed.ev.goto_line(line)

	def refresh_file_cb(self):
		ed = self.currentWidget()
		if not ed:
			return
		ed.refresh_file()

	def find_cb(self):
		ed = self.currentWidget()
		if not ed:
			return
		res = DialogManager.show_find_dialog(self.get_current_word())
		if (res == None):
			return
		(text, opt) = res
		if (text == None):
			return
		self.f_text = text
		self.f_opt = opt
		self.find_text(opt['cursor'], opt['fw'])

	def find_text(self, from_cursor, is_fw):
		if (self.f_text == None):
			return
		ed = self.currentWidget()
		if not ed:
			return
		text = self.f_text
		opt = self.f_opt
		if (from_cursor):
			if (is_fw):
				(line, inx) = (-1, -1)
			else:
				(line, inx) = ed.ev.getCursorPosition()
				if (ed.ev.hasSelectedText()):
					inx = inx - 1
			if ed.ev.findFirst(text, opt['re'], opt['cs'], opt['wo'], False, is_fw, line, inx):
				return True
			if not DialogManager.show_yes_no('End of document reached. Continue from beginning?'):
				return False
		if (is_fw):
			(line, inx) = (0, 0)
		else:
			(line, inx) = (ed.ev.lines(), 0)
		if ed.ev.findFirst(text, opt['re'], opt['cs'], opt['wo'], False, is_fw, line, inx):
			return
		DialogManager.show_msg_dialog("Could not find " + "'" + text + "'")

	def find_next_cb(self):
		self.find_text(True, True)

	def find_prev_cb(self):
		self.find_text(True, False)

	def change_ev_font(self, font):
		if font == self.ev_font:	
			return
		self.ev_font = font
		for inx in range(self.count()):
			ed = self.widget(inx)
			ed.ev.set_font(self.ev_font)

	def show_line_number_cb(self):
		val = self.m_show_line_num.isChecked()
		self.is_show_line = val
		for inx in range(self.count()):
			ed = self.widget(inx)
			ed.ev.show_line_number_cb(val)

	def show_line_number_pref(self, val):
		if val == self.m_show_line_num.isChecked():
			return
		self.m_show_line_num.setChecked(val)
		self.show_line_number_cb()

	def show_folds_cb(self):
		val = self.m_show_folds.isChecked()
		self.is_show_folds = val
		for inx in range(self.count()):
			ed = self.widget(inx)
			ed.ev.show_folds_cb(val)

	def toggle_folds_cb(self):
		ed = self.currentWidget()
		if not ed:
			return
		if self.is_show_folds:
			ed.ev.toggle_folds_cb()

	def open_in_external_editor(self, cmd):
		if not cmd:
			DialogManager.show_msg_dialog('Please configure external editor')
			return
		(f, l) = self.get_current_file_line()
		if not f:
			return
		cmd = cmd.replace('%F', f).replace('%L', str(l))
		if not QProcess.startDetached(cmd):
			DialogManager.show_msg_dialog('Failed to start: ' + cmd)

	def codemark_add(self, filename, line):
		ed = self.search_already_opened_files(filename)
		if ed:
			ed.ev.codemark_add(line)

	def codemark_del(self, filename, line):
		ed = self.search_already_opened_files(filename)
		if ed:
			ed.ev.codemark_del(line)

	def bookmark_prev_cb(self):
		ed = self.currentWidget()
		if ed:
			ed.ev.goto_marker(False)

	def bookmark_next_cb(self):
		ed = self.currentWidget()
		if ed:
			ed.ev.goto_marker(True)
			
	def save_file_at_inx(self, inx):
		if inx < 0:
			return
		page = self.widget(inx)
		filename = page.get_filename()
		if filename:
			page.ev.save_file(filename)
			page.fcv.rerun(filename)

	def save_current_page(self):
		inx = self.currentIndex()
		self.save_file_at_inx(inx)

	def save_tab_list(self, inx_list):
		for inx in inx_list:
			self.save_file_at_inx(inx)

	def save_all_file(self):
		inx_list = range(self.count())
		self.save_tab_list(inx_list)

	def page_modified_cb(self, isModifiled):
		inx = self.currentIndex()
		filename = self.tabText(inx)
		# Sign modified.
		if isModifiled:
			self.tabBar().setTabTextColor(inx, Qt.red)
		else:
			self.tabBar().setTabTextColor(inx, Qt.black)
	
	def close_cb(self, inx):
		page = self.widget(self.currentIndex())
		if page.ev.isModified():
			if DialogManager.show_yes_no("Do you want to save file ?"):
				self.save_current_page()
		self.removeTab(inx)

	def has_modified_file(self, inx_list):
		for i in inx_list:
			page = self.widget(i)
			if page.ev.isModified():
				return True
		return False

	def close_list_common(self, type):
		inx_list = self.tab_list(self.currentIndex(), type)
		if len(inx_list) == 0:
			return
		if self.has_modified_file(inx_list):
			msg = 'Closing all %s.\nSave changes ?' % type
			if DialogManager.show_yes_no(msg):
				self.save_tab_list()
			self.remove_tab_list(inx_list)
		else:
			msg = 'Close all %s ?' % type
			if not DialogManager.show_yes_no(msg):
				return
			self.remove_tab_list(inx_list)

#	def show_file_line(self, filename, line, hist=True):
#		EditorBook.show_file_line(self, filename, line, hist=hist)
#		page = self.currentWidget()
#		# modified signal callback
#		#page.ev.sig_file_modified.connect(self.page_modified_cb)
		
	# redo editing callback
	def redo_edit_cb(self):
		ed = self.currentWidget()
		if ed:
			ed.ev.redo()

	# undo editing callback
	def undo_edit_cb(self):
		ed = self.currentWidget()
		if ed:
			ed.ev.undo()

	# edting callbacks
	def cut_edit_cb(self):
		ed = self.currentWidget()
		if ed:
			ed.ev.cut()

	def paste_edit_cb(self):
		ed = self.currentWidget()
		if ed:
			ed.ev.paste()

